/**
 * Copyright (c) Tiny Technologies, Inc. All rights reserved.
 * Licensed under the LGPL or a commercial license.
 * For LGPL see License.txt in the project root for license information.
 * For commercial licenses see https://www.tiny.cloud/
 *
 * Version: 5.0.14 (2019-08-19)
 */
(function (domGlobals) {
  "use strict";

  var global = tinymce.util.Tools.resolve("tinymce.PluginManager");

  var constant = function (value) {
    return function () {
      return value;
    };
  };
  var never = constant(false);
  var always = constant(true);

  var never$1 = never;
  var always$1 = always;
  var none = function () {
    return NONE;
  };
  var NONE = (function () {
    var eq = function (o) {
      return o.isNone();
    };
    var call = function (thunk) {
      return thunk();
    };
    var id = function (n) {
      return n;
    };
    var noop = function () {};
    var nul = function () {
      return null;
    };
    var undef = function () {
      return undefined;
    };
    var me = {
      fold: function (n, s) {
        return n();
      },
      is: never$1,
      isSome: never$1,
      isNone: always$1,
      getOr: id,
      getOrThunk: call,
      getOrDie: function (msg) {
        throw new Error(msg || "error: getOrDie called on none.");
      },
      getOrNull: nul,
      getOrUndefined: undef,
      or: id,
      orThunk: call,
      map: none,
      ap: none,
      each: noop,
      bind: none,
      flatten: none,
      exists: never$1,
      forall: always$1,
      filter: none,
      equals: eq,
      equals_: eq,
      toArray: function () {
        return [];
      },
      toString: constant("none()"),
    };
    if (Object.freeze) {
      Object.freeze(me);
    }
    return me;
  })();
  var some = function (a) {
    var constant_a = function () {
      return a;
    };
    var self = function () {
      return me;
    };
    var map = function (f) {
      return some(f(a));
    };
    var bind = function (f) {
      return f(a);
    };
    var me = {
      fold: function (n, s) {
        return s(a);
      },
      is: function (v) {
        return a === v;
      },
      isSome: always$1,
      isNone: never$1,
      getOr: constant_a,
      getOrThunk: constant_a,
      getOrDie: constant_a,
      getOrNull: constant_a,
      getOrUndefined: constant_a,
      or: self,
      orThunk: self,
      map: map,
      ap: function (optfab) {
        return optfab.fold(none, function (fab) {
          return some(fab(a));
        });
      },
      each: function (f) {
        f(a);
      },
      bind: bind,
      flatten: constant_a,
      exists: bind,
      forall: bind,
      filter: function (f) {
        return f(a) ? me : NONE;
      },
      equals: function (o) {
        return o.is(a);
      },
      equals_: function (o, elementEq) {
        return o.fold(never$1, function (b) {
          return elementEq(a, b);
        });
      },
      toArray: function () {
        return [a];
      },
      toString: function () {
        return "some(" + a + ")";
      },
    };
    return me;
  };
  var from = function (value) {
    return value === null || value === undefined ? NONE : some(value);
  };
  var Option = {
    some: some,
    none: none,
    from: from,
  };

  var typeOf = function (x) {
    if (x === null) {
      return "null";
    }
    var t = typeof x;
    if (
      t === "object" &&
      (Array.prototype.isPrototypeOf(x) ||
        (x.constructor && x.constructor.name === "Array"))
    ) {
      return "array";
    }
    if (
      t === "object" &&
      (String.prototype.isPrototypeOf(x) ||
        (x.constructor && x.constructor.name === "String"))
    ) {
      return "string";
    }
    return t;
  };
  var isType = function (type) {
    return function (value) {
      return typeOf(value) === type;
    };
  };
  var isFunction = isType("function");

  var slice = Array.prototype.slice;
  var exists = function (xs, pred) {
    return findIndex(xs, pred).isSome();
  };
  var map = function (xs, f) {
    var len = xs.length;
    var r = new Array(len);
    for (var i = 0; i < len; i++) {
      var x = xs[i];
      r[i] = f(x, i, xs);
    }
    return r;
  };
  var findIndex = function (xs, pred) {
    for (var i = 0, len = xs.length; i < len; i++) {
      var x = xs[i];
      if (pred(x, i, xs)) {
        return Option.some(i);
      }
    }
    return Option.none();
  };
  var from$1 = isFunction(Array.from)
    ? Array.from
    : function (x) {
        return slice.call(x);
      };

  var contains = function (str, substr) {
    return str.indexOf(substr) !== -1;
  };

  var emojiMatches = function (emoji, lowerCasePattern) {
    return (
      contains(emoji.title.toLowerCase(), lowerCasePattern) ||
      exists(emoji.keywords, function (k) {
        return contains(k.toLowerCase(), lowerCasePattern);
      })
    );
  };
  var emojisFrom = function (list, pattern, maxResults) {
    var matches = [];
    var lowerCasePattern = pattern.toLowerCase();
    var reachedLimit = maxResults.fold(
      function () {
        return never;
      },
      function (max) {
        return function (size) {
          return size >= max;
        };
      }
    );
    for (var i = 0; i < list.length; i++) {
      if (pattern.length === 0 || emojiMatches(list[i], lowerCasePattern)) {
        matches.push({
          value: list[i].char,
          text: list[i].title,
          icon: list[i].char,
        });
        if (reachedLimit(matches.length)) {
          break;
        }
      }
    }
    return matches;
  };

  var init = function (editor, database) {
    editor.ui.registry.addAutocompleter("emoticons", {
      ch: ":",
      columns: "auto",
      minChars: 2,
      fetch: function (pattern, maxResults) {
        return database.waitForLoad().then(function () {
          var candidates = database.listAll();
          return emojisFrom(candidates, pattern, Option.some(maxResults));
        });
      },
      onAction: function (autocompleteApi, rng, value) {
        editor.selection.setRng(rng);
        editor.insertContent(value);
        autocompleteApi.hide();
      },
    });
  };

  var Cell = function (initial) {
    var value = initial;
    var get = function () {
      return value;
    };
    var set = function (v) {
      value = v;
    };
    var clone = function () {
      return Cell(get());
    };
    return {
      get: get,
      set: set,
      clone: clone,
    };
  };

  var last = function (fn, rate) {
    var timer = null;
    var cancel = function () {
      if (timer !== null) {
        domGlobals.clearTimeout(timer);
        timer = null;
      }
    };
    var throttle = function () {
      var args = [];
      for (var _i = 0; _i < arguments.length; _i++) {
        args[_i] = arguments[_i];
      }
      if (timer !== null) {
        domGlobals.clearTimeout(timer);
      }
      timer = domGlobals.setTimeout(function () {
        fn.apply(null, args);
        timer = null;
      }, rate);
    };
    return {
      cancel: cancel,
      throttle: throttle,
    };
  };

  var insertEmoticon = function (editor, ch) {
    editor.insertContent(ch);
  };

  var hasOwnProperty = Object.prototype.hasOwnProperty;
  var shallow = function (old, nu) {
    return nu;
  };
  var baseMerge = function (merger) {
    return function () {
      var objects = new Array(arguments.length);
      for (var i = 0; i < objects.length; i++) {
        objects[i] = arguments[i];
      }
      if (objects.length === 0) {
        throw new Error("Can't merge zero objects");
      }
      var ret = {};
      for (var j = 0; j < objects.length; j++) {
        var curObject = objects[j];
        for (var key in curObject) {
          if (hasOwnProperty.call(curObject, key)) {
            ret[key] = merger(ret[key], curObject[key]);
          }
        }
      }
      return ret;
    };
  };
  var merge = baseMerge(shallow);

  var keys = Object.keys;
  var hasOwnProperty$1 = Object.hasOwnProperty;
  var each = function (obj, f) {
    var props = keys(obj);
    for (var k = 0, len = props.length; k < len; k++) {
      var i = props[k];
      var x = obj[i];
      f(x, i, obj);
    }
  };
  var map$1 = function (obj, f) {
    return tupleMap(obj, function (x, i, obj) {
      return {
        k: i,
        v: f(x, i, obj),
      };
    });
  };
  var tupleMap = function (obj, f) {
    var r = {};
    each(obj, function (x, i) {
      var tuple = f(x, i, obj);
      r[tuple.k] = tuple.v;
    });
    return r;
  };
  var has = function (obj, key) {
    return hasOwnProperty$1.call(obj, key);
  };

  var global$1 = tinymce.util.Tools.resolve("tinymce.Resource");

  var global$2 = tinymce.util.Tools.resolve("tinymce.util.Delay");

  var global$3 = tinymce.util.Tools.resolve("tinymce.util.Promise");

  var DEFAULT_ID = "tinymce.plugins.emoticons";
  var getEmoticonDatabaseUrl = function (editor, pluginUrl) {
    return editor.getParam(
      "emoticons_database_url",
      pluginUrl + "/js/emojis" + editor.suffix + ".js"
    );
  };
  var getEmoticonDatabaseId = function (editor) {
    return editor.getParam("emoticons_database_id", DEFAULT_ID, "string");
  };
  var getAppendedEmoticons = function (editor) {
    return editor.getParam("emoticons_append", {}, "object");
  };
  var Settings = {
    getEmoticonDatabaseUrl: getEmoticonDatabaseUrl,
    getEmoticonDatabaseId: getEmoticonDatabaseId,
    getAppendedEmoticons: getAppendedEmoticons,
  };

  var ALL_CATEGORY = "All";
  var categoryNameMap = {
    symbols: "Symbols",
    people: "People",
    animals_and_nature: "Animals and Nature",
    food_and_drink: "Food and Drink",
    activity: "Activity",
    travel_and_places: "Travel and Places",
    objects: "Objects",
    flags: "Flags",
    user: "User Defined",
  };
  var translateCategory = function (categories, name) {
    return has(categories, name) ? categories[name] : name;
  };
  var getUserDefinedEmoticons = function (editor) {
    var userDefinedEmoticons = Settings.getAppendedEmoticons(editor);
    return map$1(userDefinedEmoticons, function (value) {
      return merge(
        {
          keywords: [],
          category: "user",
        },
        value
      );
    });
  };
  var initDatabase = function (editor, databaseUrl, databaseId) {
    var categories = Cell(Option.none());
    var all = Cell(Option.none());
    var processEmojis = function (emojis) {
      var cats = {};
      var everything = [];
      each(emojis, function (lib, title) {
        var entry = {
          title: title,
          keywords: lib.keywords,
          char: lib.char,
          category: translateCategory(categoryNameMap, lib.category),
        };
        var current =
          cats[entry.category] !== undefined ? cats[entry.category] : [];
        cats[entry.category] = current.concat([entry]);
        everything.push(entry);
      });
      categories.set(Option.some(cats));
      all.set(Option.some(everything));
    };
    editor.on("init", function () {
      global$1.load(databaseId, databaseUrl).then(
        function (emojis) {
          var userEmojis = getUserDefinedEmoticons(editor);
          processEmojis(merge(emojis, userEmojis));
        },
        function (err) {
          domGlobals.console.log("Failed to load emoticons: " + err);
          categories.set(Option.some({}));
          all.set(Option.some([]));
        }
      );
    });
    var listCategory = function (category) {
      if (category === ALL_CATEGORY) {
        return listAll();
      }
      return categories
        .get()
        .bind(function (cats) {
          return Option.from(cats[category]);
        })
        .getOr([]);
    };
    var listAll = function () {
      return all.get().getOr([]);
    };
    var listCategories = function () {
      return [ALL_CATEGORY].concat(keys(categories.get().getOr({})));
    };
    var waitForLoad = function () {
      if (hasLoaded()) {
        return global$3.resolve(true);
      } else {
        return new global$3(function (resolve, reject) {
          var numRetries = 3;
          var interval = global$2.setInterval(function () {
            if (hasLoaded()) {
              global$2.clearInterval(interval);
              resolve(true);
            } else {
              numRetries--;
              if (numRetries < 0) {
                domGlobals.console.log(
                  "Could not load emojis from url: " + databaseUrl
                );
                global$2.clearInterval(interval);
                reject(false);
              }
            }
          }, 500);
        });
      }
    };
    var hasLoaded = function () {
      return categories.get().isSome() && all.get().isSome();
    };
    return {
      listCategories: listCategories,
      hasLoaded: hasLoaded,
      waitForLoad: waitForLoad,
      listAll: listAll,
      listCategory: listCategory,
    };
  };

  var patternName = "pattern";
  var open = function (editor, database) {
    var initialState = {
      pattern: "",
      results: emojisFrom(database.listAll(), "", Option.some(300)),
    };
    var currentTab = Cell(ALL_CATEGORY);
    var scan = function (dialogApi) {
      var dialogData = dialogApi.getData();
      var category = currentTab.get();
      var candidates = database.listCategory(category);
      var results = emojisFrom(
        candidates,
        dialogData[patternName],
        category === ALL_CATEGORY ? Option.some(300) : Option.none()
      );
      dialogApi.setData({ results: results });
    };
    var updateFilter = last(function (dialogApi) {
      scan(dialogApi);
    }, 200);
    var searchField = {
      label: "Search",
      type: "input",
      name: patternName,
    };
    var resultsField = {
      type: "collection",
      name: "results",
    };
    var getInitialState = function () {
      var body = {
        type: "tabpanel",
        tabs: map(database.listCategories(), function (cat) {
          return {
            title: cat,
            name: cat,
            items: [searchField, resultsField],
          };
        }),
      };
      return {
        title: "Emoticons",
        size: "normal",
        body: body,
        initialData: initialState,
        onTabChange: function (dialogApi, details) {
          currentTab.set(details.newTabName);
          updateFilter.throttle(dialogApi);
        },
        onChange: updateFilter.throttle,
        onAction: function (dialogApi, actionData) {
          if (actionData.name === "results") {
            insertEmoticon(editor, actionData.value);
            dialogApi.close();
          }
        },
        buttons: [
          {
            type: "cancel",
            text: "Close",
            primary: true,
          },
        ],
      };
    };
    var dialogApi = editor.windowManager.open(getInitialState());
    dialogApi.focus(patternName);
    if (!database.hasLoaded()) {
      dialogApi.block("Loading emoticons...");
      database
        .waitForLoad()
        .then(function () {
          dialogApi.redial(getInitialState());
          updateFilter.throttle(dialogApi);
          dialogApi.focus(patternName);
          dialogApi.unblock();
        })
        .catch(function (err) {
          dialogApi.redial({
            title: "Emoticons",
            body: {
              type: "panel",
              items: [
                {
                  type: "alertbanner",
                  level: "error",
                  icon: "warning",
                  text: "<p>Could not load emoticons</p>",
                },
              ],
            },
            buttons: [
              {
                type: "cancel",
                text: "Close",
                primary: true,
              },
            ],
            initialData: {
              pattern: "",
              results: [],
            },
          });
          dialogApi.focus(patternName);
          dialogApi.unblock();
        });
    }
  };
  var Dialog = { open: open };

  var register = function (editor, database) {
    var onAction = function () {
      return Dialog.open(editor, database);
    };
    editor.ui.registry.addButton("emoticons", {
      tooltip: "Emoticons",
      icon: "emoji",
      onAction: onAction,
    });
    editor.ui.registry.addMenuItem("emoticons", {
      text: "Emoticons...",
      icon: "emoji",
      onAction: onAction,
    });
  };
  var Buttons = { register: register };

  function Plugin() {
    global.add("emoticons", function (editor, pluginUrl) {
      var databaseUrl = Settings.getEmoticonDatabaseUrl(editor, pluginUrl);
      var databaseId = Settings.getEmoticonDatabaseId(editor);
      var database = initDatabase(editor, databaseUrl, databaseId);
      Buttons.register(editor, database);
      init(editor, database);
    });
  }

  Plugin();
})(window);
